﻿using UnityEngine;
using System.Collections;

public class GridGenerator : MonoBehaviour {

	public float level_difficulty = 1f;
	public int seed = 0;
	
	private int width;
	private int height;
	private int[] grid;  // platformit
	private int[] grid2; // perusblokkisysteemi
	private int[] grid3; // perusblokkisysteemi
	private bool[] grid_x_flip;
	private bool[] grid_y_flip;
	
	private int x;
	private int y;
	private int last_x;
	private int last_y;
	private int steps;
	private int originalsteps;
	
	private int bound_l;
	private int bound_u;
	private int bound_r;
	private int bound_d;
	
	public float world_scale = 5f;
	
	public GameObject platform_h;
	public GameObject platform_v;
	
	public GameObject world_bounds_box;
	public GameObject[] tiles;
	
	
	public GameObject level_start;
	public GameObject level_finish;
	public GameObject[] level_objects;
	public int[] level_object_freq;
	public static int[] level_object_i;
	public float[] level_object_difficulty;
	
	private int gravitate_up = 0;
	private int gravitate_right = 0;
	
	public int GetSteps()
	{
		return originalsteps;
	}
	
	
	// Use this for initialization
	void Start () {
	
		
	
		if(seed==0)
			seed = Random.Range(0,9999);
		Random.seed = seed;
		
		//level_difficulty = Random.Range(1,30);
	

		
		// difficulty effects item frequency
		for(int i=0;i<level_object_freq.Length;i++)
		{
			level_object_difficulty[i] *= level_difficulty;
			
			level_object_freq[i] += (int)level_object_difficulty[i];
			
			if(level_object_freq[i] < level_object_freq.Length)
			{
				level_object_freq[i] = level_object_freq.Length;
				print ("Max freq reached for object "+i);
			}
		}
		
		// item starting phase
		if(level_object_i == null)
		{
			level_object_i = new int[level_objects.Length];
			for(int i=0;i<level_object_i.Length;i++)
			{
				level_object_i[i] = Random.Range(0,level_object_freq[i]);
				i++;
			}
		}
		// satunnainen koko
		int gstart_x=0;
		int gstart_y=0;
		
		if(Random.Range(0,100)<25)
		{
			// pien neliö
			width = 10;
			height = 10;
			
			steps = 20;
			originalsteps = steps;
			
			// lähtöpiste
			gstart_x=width/2;
			gstart_y=height-5;
		}
		else if(Random.Range(0,100)<33)
		{
			// keskipitkä tunneli
			width = 60;
			height = 7;
			
			steps = 60;
			originalsteps = steps;
			
			// lähtöpiste
			gstart_x=4;
			gstart_y=height/2;
			
			gravitate_right = 30;
		}
		else if(Random.Range(0,100)<50)
		{
			// keskipitkä kuilu
			width = 7;
			height = 60;
			
			steps = 40;
			originalsteps = steps;
			
			// lähtöpiste
			gstart_x=width/2;
			
			if(Random.Range(0,100)<50)
			{
				gstart_y=4;
				gravitate_up=30;
			}
			else
			{
				gstart_y=height-4;
				gravitate_up=-30;
			}
		}
		else
		{
			width = (int)((float)Random.Range(6,50) * (1f+level_difficulty/100f));
			height = (int)((float)Random.Range(6,50) * (1f+level_difficulty/100f));
			
			int min_steps=(int)Mathf.Sqrt(width*width+height*height);
			int max_steps=(int)((width+height)*1.5f);
			steps = Random.Range(min_steps,max_steps);
			originalsteps = steps;
			
			// lähtöpiste
			gstart_x=4;
			gstart_y=height/2;
			
			gravitate_right = (width-height);
			gravitate_up = -(height-width);
		}
		
		
		bound_l = width;
		bound_r = 0;
		bound_d = height;
		bound_u = 0;
		
		
		

		/*
		print ("################################ ");
		print ("Generating level with seed: "+seed);
		print ("Difficulty: "+level_difficulty);
		print ("Steps: "+steps);
		*/
		// nollaus
		grid = new int[width*height];
		grid2 = new int[width*2*height*2];
		grid3 = new int[width*2*height*2];
		grid_x_flip = new bool[width*2*height*2];
		grid_y_flip = new bool[width*2*height*2];
		
		for(x=0;x<width;x++)
		{
			for(y=0;y<height;y++)
			{
				grid[x+(y*width)]=0;
			}
		}
		for(x=0;x<width*2;x++)
		{
			for(y=0;y<height*2;y++)
			{
				grid_x_flip[x+(y*width*2)]=false;
				grid_y_flip[x+(y*width*2)]=false;
				
				// ground fill, 2 version
				if(Random.Range(0,100)<30)
					grid2[x+(y*width*2)]=4;
				else
					grid2[x+(y*width*2)]=5;
				
				grid3[x+(y*width*2)] = grid2[x+(y*width*2)];
			}
		}
		
		// lähtöpiste
		x=gstart_x;
		y=gstart_y;
		bool start_pos_set = false;
		
		// generointi
		while(steps>0)
		{
			last_x = x;
			last_y = y;
			
			if(Step ())
				steps--;
				
			bool visited_before = false;
			if(IsFilledGround(x*2,y*2)==false)
				visited_before = true;
			
			GenerateGrid();
			
			if(start_pos_set == false)
			{
				//level_start.transform.position = new Vector3((x+0.25f)*world_scale,(y+0.25f)*world_scale,0f);
				level_start.transform.position = new Vector3(x*world_scale,y*world_scale,0f);
				start_pos_set = true;
				
				level_finish.transform.position = level_start.transform.position;
			}
			
			if(visited_before==false)
				PlotLevelObjects();
		}
		Build();
		BuildPlatforms();
		WorldLimits();
	}
	void WorldLimits()
	{
		
		GameObject wlimit_l = Instantiate(world_bounds_box,transform.position,transform.rotation)as GameObject;
		GameObject wlimit_r = Instantiate(world_bounds_box,transform.position,transform.rotation)as GameObject;
		GameObject wlimit_u = Instantiate(world_bounds_box,transform.position,transform.rotation)as GameObject;
		GameObject wlimit_d = Instantiate(world_bounds_box,transform.position,transform.rotation)as GameObject;
		
		wlimit_l.transform.parent = transform;
		wlimit_r.transform.parent = transform;
		wlimit_u.transform.parent = transform;
		wlimit_d.transform.parent = transform;
		
		
		wlimit_l.transform.position = new Vector3(bound_l-1.25f*5.5f, (bound_u+bound_d)/2);
		wlimit_l.transform.position = wlimit_l.transform.position * world_scale;
		wlimit_l.transform.localScale = wlimit_l.transform.localScale * world_scale*12;
		
		wlimit_r.transform.position = new Vector3(bound_r+1.25f*5.5f, (bound_u+bound_d)/2);
		wlimit_r.transform.position = wlimit_r.transform.position * world_scale;
		wlimit_r.transform.localScale = wlimit_r.transform.localScale * world_scale*12;
		
		wlimit_u.transform.position = new Vector3((bound_l+bound_r)/2, bound_u+1.25f*5.5f);
		wlimit_u.transform.position = wlimit_u.transform.position * world_scale;
		wlimit_u.transform.localScale = wlimit_u.transform.localScale * world_scale*12;
		
		wlimit_d.transform.position = new Vector3((bound_l+bound_r)/2, bound_d-1.25f*5.5f);
		wlimit_d.transform.position = wlimit_d.transform.position * world_scale;
		wlimit_d.transform.localScale = wlimit_d.transform.localScale * world_scale*12;
		
		
		wlimit_l.transform.localScale = new Vector3(wlimit_l.transform.localScale.x,
		                                            wlimit_l.transform.localScale.y*2,
		                                            wlimit_l.transform.localScale.z);
		wlimit_r.transform.localScale = new Vector3(wlimit_r.transform.localScale.x,
		                                            wlimit_r.transform.localScale.y*2,
		                                            wlimit_r.transform.localScale.z);
		wlimit_u.transform.localScale = new Vector3(wlimit_u.transform.localScale.x*2,
		                                            wlimit_u.transform.localScale.y,
		                                            wlimit_u.transform.localScale.z);
		wlimit_d.transform.localScale = new Vector3(wlimit_d.transform.localScale.x*2,
		                                            wlimit_d.transform.localScale.y,
		                                            wlimit_d.transform.localScale.z);
	}
	void PlotLevelObjects()
	{
		for(int i=0; i < level_object_freq.Length; i++)
		{
			level_object_i[i]++;
			
			if(level_object_i[i] >= level_object_freq[i])
			{
				level_object_i[i]=0;
				
				level_object_i[i] = Random.Range(0,level_object_freq[i]/4);
				
				GameObject ob=null;
				ob = Instantiate(level_objects[i],new Vector3(x,y,0),transform.rotation)as GameObject;
				ob.transform.parent = transform;
				ob.transform.position += new Vector3(0.25f,0.25f,0f);
				ob.transform.position = ob.transform.position * world_scale;
				//ob.transform.position += new Vector3(world_scale,world_scale,0f);
			}
		}
	}
	void GenerateGrid()
	{
		x*=2;
		y*=2;
		width*=2;
		grid2[(x)+((y)*width)] = 15; // emptiness
		grid3[(x)+((y)*width)]=grid2[(x)+((y)*width)];
		
		grid2[(x+1)+((y)*width)] = 15; // emptiness
		grid3[(x+1)+((y)*width)]=grid2[(x+1)+((y)*width)];
		
		grid2[(x+1)+((y+1)*width)] = 15; // emptiness
		grid3[(x+1)+((y+1)*width)]=grid2[(x+1)+((y+1)*width)];
		
		grid2[(x)+((y+1)*width)] = 15; // emptiness
		grid3[(x)+((y+1)*width)]=grid2[(x)+((y+1)*width)];
		
		x/=2;
		y/=2;
		width/=2;
		
		// ylös
		if(y>last_y)
		{
			if(grid[x+(y*width)] == 0)
				grid[x+(y*width)]=1;
			
			if(grid[x+(y*width)] == 2)
				grid[x+(y*width)]=3;
		}
		// alas
		if(y<last_y)
		{
			if(grid[last_x+(last_y*width)] == 0)
				grid[last_x+(last_y*width)]=1;
			
			if(grid[last_x+(last_y*width)] == 2)
				grid[last_x+(last_y*width)]=3;
		}
		// oikeelle
		if(x>last_x)
		{
			if(grid[last_x+(last_y*width)] == 0)
				grid[last_x+(last_y*width)]=2;
			
			if(grid[last_x+(last_y*width)] == 1)
				grid[last_x+(last_y*width)]=3;
		}
		// vasemmalle
		if(x<last_x)
		{
			if(grid[x+(y*width)] == 0)
				grid[x+(y*width)]=2;
			
			if(grid[x+(y*width)] == 1)
				grid[x+(y*width)]=3;
		}
	}
	void PlotTile(int xx, int yy, int i)
	{
		if(i >= tiles.Length)
		{
			// emptiness
		}
		else if(tiles[i]!=null)
		{
			Vector3 flipper = new Vector3(1f,1f,1f);
			if(grid_x_flip[xx+(yy*width)])
				flipper.x = -1f;
			if(grid_y_flip[xx+(yy*width)])
				flipper.y = -1f;
			
			GameObject tile;
			tile = Instantiate(tiles[i],new Vector3(xx,yy,0),transform.rotation)as GameObject;
			tile.transform.parent = transform;
			tile.transform.localScale = Vector3.Scale(tile.transform.localScale , flipper) * world_scale;
			tile.transform.position = tile.transform.position * world_scale;
		}
	}
	bool IsFilledGround(int xx, int yy)
	{
		if(grid3[(xx) + ((yy)*width)]==4)return true;
		if(grid3[(xx) + ((yy)*width)]==5)return true;
		
		return false;
	}
	void Build()
	{
		bound_l*=2;
		bound_r*=2;
		bound_u*=2;
		bound_d*=2;
		width*=2;
		height*=2;
		world_scale/=2;
		for(x=bound_l-1;x<=bound_r+1;x++)
		{
			for(y=bound_d-1;y<=bound_u+1;y++)
			{
				// ground
				if(IsFilledGround(x,y)==false)
				{

					// 0 = maan ja seinän välinen vasen sisänurkka
					if(IsFilledGround(x-1,y)==true &&
					   IsFilledGround(x+1,y)==false &&
					   IsFilledGround(x,y+1)==false &&
					   IsFilledGround(x,y-1)==true)
						grid2[x+(y*width)]=0;
						
					// 0 = maan ja seinän välinen oikea sisänurkka
					if(IsFilledGround(x+1,y)==true &&
					   IsFilledGround(x-1,y)==false &&
					   IsFilledGround(x,y+1)==false &&
					   IsFilledGround(x,y-1)==true)
						{
						grid2[x+(y*width)]=0;
						grid_x_flip[x+(y*width)]=true;
						}
					// 1 = maa
					if(IsFilledGround(x,y-1)==true &&
					   IsFilledGround(x-1,y)==false &&
					   IsFilledGround(x+1,y)==false)
						grid2[x+(y*width)]=1;
						
					// 2 = maan vasen ulkonurkka
					if(IsFilledGround(x-1,y-1)==true &&
					   IsFilledGround(x-1,y)==false &&
					   IsFilledGround(x,y-1)==false)
						grid2[x+(y*width)]=2;
					// 2 = maan oikea ulkonurkka
					if(IsFilledGround(x+1,y-1)==true &&
					   IsFilledGround(x+1,y)==false &&
					   IsFilledGround(x,y-1)==false)
					   {
						grid2[x+(y*width)]=2;
						grid_x_flip[x+(y*width)]=true;
					   }
						
					// 6 = vasen seinä
					if(IsFilledGround(x-1,y)==true &&
					   IsFilledGround(x,y+1)==false &&
					   IsFilledGround(x,y-1)==false)
						grid2[x+(y*width)]=6;
						
					// 6 = oikea seinä
					if(IsFilledGround(x+1,y)==true &&
					   IsFilledGround(x,y+1)==false &&
					   IsFilledGround(x,y-1)==false)
					   {
						grid2[x+(y*width)]=6;
						grid_x_flip[x+(y*width)]=true;
					   }
						
					// 8 = katon vasen sisänurkka
					if(IsFilledGround(x-1,y)==true &&
					   IsFilledGround(x+1,y)==false &&
					   IsFilledGround(x,y+1)==true &&
					   IsFilledGround(x,y-1)==false)
						grid2[x+(y*width)]=8;
						
					// 8 = katon oikea sisänurkka
					if(IsFilledGround(x+1,y)==true &&
					   IsFilledGround(x-1,y)==false &&
					   IsFilledGround(x,y+1)==true &&
					   IsFilledGround(x,y-1)==false)
					{
						grid2[x+(y*width)]=8;
						grid_x_flip[x+(y*width)]=true;
					}
					
					// 9 = katto
					if(IsFilledGround(x,y+1)==true &&
					   IsFilledGround(x-1,y)==false &&
					   IsFilledGround(x+1,y)==false)
						grid2[x+(y*width)]=9;
						
					// 10 = katon vasen ulkourkka
					if(IsFilledGround(x-1,y)==false &&
					   IsFilledGround(x,y+1)==false &&
					   IsFilledGround(x-1,y+1)==true)
						grid2[x+(y*width)]=10;
					
					// 10 = katon oikea ulkourkka
					if(IsFilledGround(x+1,y)==false &&
					   IsFilledGround(x,y+1)==false &&
					   IsFilledGround(x+1,y+1)==true)
					{
						grid2[x+(y*width)]=10;
						grid_x_flip[x+(y*width)]=true;
					}
					

					
						

				}	
			}
		}
		
		
		for(x=bound_l-1;x<=bound_r+1;x++)
		{
			for(y=bound_d-1;y<=bound_u+1;y++)
			{
				PlotTile(x,y,grid2[x+(y*width)]);
			}
		}
		bound_l/=2;
		bound_r/=2;
		bound_u/=2;
		bound_d/=2;
		width/=2;
		height/=2;
		world_scale*=2;
	}
	
	
	void BuildPlatforms()
	{
	
		width *= 2; // IsFilledGround tarvii tätä
		
		for(x=bound_l-1;x<=bound_r+1;x+=1)
		{
			for(y=bound_d-1;y<=bound_u+1;y+=1)
			{
				if(x>=bound_l || y>=bound_d || x<=bound_r || y<=bound_u)
				{
					if(IsFilledGround((x)*2,(y)*2)==false)
					{
						GameObject bf=null;
						GameObject bw=null;
						
						// 0 = alhaalla ja oikeella
						if(grid[x+(y*(width/2))]==0)
						{
							if(IsFilledGround((x)*2,(y-1)*2)==false)
								bf = Instantiate(platform_h,new Vector3(x,y,0),transform.rotation) as GameObject;
							
							if(IsFilledGround((x+1)*2,(y)*2)==false)
								bw = Instantiate(platform_v,new Vector3(x,y,0),transform.rotation) as GameObject;
						}
						// 1 = oikeella
						else if(grid[x+(y*(width/2))]==1)
						{
							if(IsFilledGround((x+1)*2,(y)*2)==false)
								bw = Instantiate(platform_v,new Vector3(x,y,0),transform.rotation) as GameObject;
						}
						// 2 = alhaalla
						else if(grid[x+(y*(width/2))]==2)
						{
							if(IsFilledGround((x)*2,(y-1)*2)==false)
								bf = Instantiate(platform_h,new Vector3(x,y,0),transform.rotation) as GameObject;
						}
						// 3 = ei missään
						else if(grid[x+(y*(width/2))]==3)
						{
						}
						
						
						
						
						// skaalaus
						if(bf!=null)
						{
							bf.transform.localScale = bf.transform.localScale * world_scale;
							bf.transform.position += new Vector3(0.25f,-0.25f);
							bf.transform.position = bf.transform.position * world_scale;
							bf.transform.parent = transform;
						}
						if(bw!=null)
						{
							bw.transform.localScale = bw.transform.localScale * world_scale;
							bw.transform.position += new Vector3(0.75f,0.25f);
							bw.transform.position = bw.transform.position * world_scale;
							bw.transform.parent = transform;
						}
					}
				}
			}
		}
		
		width /= 2; // IsFilledGround tarvii tätä
		/*
	
		for(x=bound_l-1;x<=bound_r+1;x+=1)
		{
			for(y=bound_d-1;y<=bound_u+1;y+=1)
			{
				GameObject bf=null;
				GameObject bw=null;
				
				if(x<bound_l || y<bound_d || x>bound_r || y>bound_u)
				{
					//bb = Instantiate(block_b,new Vector3(x,y,0),transform.rotation) as GameObject;
				}
				else
				if(grid[x+(y*width)]==0)// alhaalla ja oikeella
				{
					
					if((grid[x-1+(y*width)]==0 || grid[x-1+(y*width)]==1) &&
					   (grid[x+((y+1)*width)]==0 || grid[x+((y+1)*width)]==2))
					{
						//bb = Instantiate(block_b,new Vector3(x,y,0),transform.rotation) as GameObject;
					}
					else
					{
						bf = Instantiate(platform_h,new Vector3(x+0.25f,y-0.25f,0),transform.rotation) as GameObject;
						bw = Instantiate(platform_v,new Vector3(x+0.75f,y+0.25f,0),transform.rotation) as GameObject;
					}
				}
				else
				if(grid[x+(y*width)]==1)// oikeella
				{
					bw = Instantiate(platform_v,new Vector3(x+0.75f,y+0.25f,0),transform.rotation) as GameObject;
				}
				else
				if(grid[x+(y*width)]==2)// alhaalla
				{
					bf = Instantiate(platform_h,new Vector3(x+0.25f,y-0.25f,0),transform.rotation) as GameObject;
				}
				else
				if(grid[x+(y*width)]==3)// ei missään
				{
					
				}

				// skaalaus
				if(bf!=null)
				{
					bf.transform.localScale = bf.transform.localScale * world_scale;
					bf.transform.position = bf.transform.position * world_scale;
				}
				if(bw!=null)
				{
					bw.transform.localScale = bw.transform.localScale * world_scale;
					bw.transform.position = bw.transform.position * world_scale;
				}
				
				//if(bb!=null)
				//{
				//	bb.transform.localScale = bb.transform.localScale * world_scale;
				//	bb.transform.position = bb.transform.position * world_scale;
				//}
				
		
			}
		}
		*/
	}
	bool Step()
	{
		if(Random.Range(0,100+(Mathf.Abs (gravitate_right)-Mathf.Abs (gravitate_up)))<50)
		{
			// X
			if(Random.Range(0,100+gravitate_right)<40 && x > 2)
				x--;
			else if(x < width-3)
				x++;
			else
				return false;
		}
		else
		{
			// Y
			if(Random.Range(0,100+gravitate_up)<50 && y > 2)
				y--;
			else if(y < height-3)
				y++;
			else
				return false;
		}
		
		
		if(x < bound_l)bound_l=x;
		if(x+1 > bound_r)bound_r=x+1;
		if(y < bound_d)bound_d=y;
		if(y+1 > bound_u)bound_u=y+1;
		
		// kaukasin mahollinen loppupiste
		if(Vector3.Distance(level_start.transform.position, level_finish.transform.position) <
		   Vector3.Distance(level_start.transform.position, new Vector3(x*world_scale,y*world_scale,0f)))
			level_finish.transform.position = new Vector3(x*world_scale,y*world_scale,0f);
		
		return true;
	}
	
	
	
	// Update is called once per frame
	void Update () {
	
		if(Input.GetKeyDown(KeyCode.R))
			Application.LoadLevel(Application.loadedLevel);
	
	}
}
